package com.photoeditor.demo.model.image.collage.templet;


import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.Region;

import com.photoeditor.demo.model.image.collage.util.MathUtil;

/**
 * 这个类用于记录一个拼接块的所有的点
 */
public class CollagePoints {

    //mlength大于等于3
    private int mLength;

    private Point mPoints[];

    private int mDragHeadIndex = -1;
    private int mDragTailIndex = -1;
    private Point mDragOffset;
    public static final float ADMISSIBLE_ERROR = 0.013f;

    // 记录移动前的一个点用于回滚操作
    private Point mCacheHeadPoint;
    private Point mCacheTailPoint;

    private static final float MIN_MARGIN = (float) (72 * 1.0 / 1080);

    public CollagePoints(Point ...points){
        mPoints = points;
        mLength = mPoints.length;
    }

    public int getLength(){
        return mLength;
    }

    public Point getPoint(int i){
        if(i < mLength){
            return mPoints[i];
        }
        return null;
    }

    public void addPoint(Point point) {
        if (mPoints == null) {
            mPoints = new Point[0];
        }
        Point[] tempPoints = new Point[mPoints.length + 1];
        System.arraycopy(mPoints, 0, tempPoints, 0, mPoints.length);
        tempPoints[tempPoints.length - 1] = point;
        mPoints = tempPoints;
        mLength = mPoints.length;
    }

    public Point getFirstPoint(){
        return mPoints[0];
    }

    public Point getSecondPoint(){
        return mPoints[1];
    }

    public Point getThirdPoint(){
        return mPoints[2];
    }

    public Point getLastPoint(){
        return mPoints[mLength - 1];
    }

    public RectF getBoundRect(){
        RectF rectF = new RectF();
        Point first = getPoint(0);
        float minX = first.x;
        float minY = first.y;
        float maxX = first.x;
        float maxY = first.y;
        for(int i = 1 ; i < mLength ; i++){
            Point point = getPoint(i);
            if(minX > point.x){
                minX = point.x;
            }
            if(minY > point.y){
                minY = point.y;
            }

            if(maxX < point.x){
                maxX = point.x;
            }
            if(maxY < point.y){
                maxY = point.y;
            }
        }
        rectF.set(minX, minY, maxX, maxY);
        return rectF;
    }

    /**
     * 这个方法会计算出 离当前点的两边 都是diatanceProgress  距离的点
     * 算法：
     * （1）通过证明得到这个点一定在当前点的角平分线上
     * （2）假设初始点微A0，平移后的点为A1，已知两条平行线间距为distance，假设A0,A1之间的间距是d，A0点两条直线的夹角是Q
     * 则有：d * sin(Q/2) = distance; 这样就可以求到d，在通过这两条线求得角平分线的斜率k，然后计算出它与X轴的夹角W，
     * 然后用d * sinW = dy, d * cosW = dx;就可以求出所要的
     * （3）再通过角平分线的下一个点，是否在Path区域内把，判断dx,dy的正负值 即可
     * （4）如果和BoundRect共边，需要先平移计算出一个新的点，然后对这点应用dx,dy得到最终点
     *
     * @param i
     * @param trueWidth
     * @param trueHeight
     * @param diatanceProgress
     * @return
     */
    public Point getCurrentPoint(int i, float trueWidth, float trueHeight, int diatanceProgress){
        if(diatanceProgress == 0){
            Point middle = getPoint(i);
            Point result = new Point(middle.x * trueWidth, middle.y * trueHeight);
            return result;
        } else {
            float distance = diatanceProgress / 100f * RoundPathUtil.MAX_DISTANCE_SIZE;
            float edx = 0, edy = 0;
            Point front;
            Point middle;
            Point after;
            if (i == 0) {
                front = getPoint(mLength - 1);
                middle = getPoint(i);
                after = getPoint(i + 1);
            } else if (i == mLength - 1) {
                front = getPoint(i - 1);
                middle = getPoint(i);
                after = getPoint(0);
            } else {
                front = getPoint(i - 1);
                middle = getPoint(i);
                after = getPoint(i + 1);
            }
            Point result = new Point(middle.x * trueWidth, middle.y * trueHeight);
            Line frontToMiddle = new Line(front.x * trueWidth, front.y * trueHeight, result.x, result.y);
            Line middleToAfter = new Line(result.x, result.y, after.x * trueWidth, after.y * trueHeight);

            //前面是通过boundRect判断dx dy的正负值的 现在修改为用Region判断
//            RectF boundRect = new RectF(mBoundRect.left * trueWidth, mBoundRect.top * trueHeight, mBoundRect.right * trueWidth, mBoundRect.bottom * trueHeight);
            Region region = getDefaultRegion(trueWidth, trueHeight);

            if (frontToMiddle.isHrizontal()) {//y不变
                if (front.y == 0f) {//frontToMiddle和上边重叠
                    edy += distance / 2;
                } else if (front.y == 1f) {//frontToMiddle和下边重叠
                    edy += -distance / 2;
                }
            } else if (frontToMiddle.isVertical()) {
                if (front.x == 0f) {//frontToMiddle和左边重叠
                    edx += distance / 2;
                } else if (front.x == 1f) {//frontToMiddle和右边重叠
                    edx += -distance / 2;
                }
            }

            if (middleToAfter.isHrizontal()) {//y不变
                if (after.y == 0f) {//middleToAfter和上边重叠
                    edy += distance / 2;
                } else if (after.y == 1f) {//middleToAfter和下边重叠
                    edy += -distance / 2;
                }
            } else if (middleToAfter.isVertical()) {
                if (after.x == 0f) {//middleToAfter和左边重叠
                    edx += distance / 2;
                } else if (after.x == 1f) {//middleToAfter和右边重叠
                    edx += -distance / 2;
                }
            }

            //这里是处理挨着边上 需要两次移动的情况
            if(edx != 0f && edy != 0f){
                result.offset(edx, edy);
            } else if(edx != 0f){
                float x, y;
                x = result.x + edx;
                if(frontToMiddle.isVertical()){//frontToMiddle是竖直的
                    if(middleToAfter.isVertical()){
                        y = result.y;
                    } else {
                        y = middleToAfter.getY(x);
                    }
                } else{//middleToAfter是竖直的
                    if(frontToMiddle.isVertical()){
                        y = result.y;
                    } else {
                        y = frontToMiddle.getY(x);
                    }
                }
                result.set(x, y);
            } else if(edy != 0f){
                float x, y;
                y = result.y + edy;
                if(frontToMiddle.isHrizontal()){//frontToMiddle是水平的
                    if(middleToAfter.isHrizontal()){
                        x = result.x;
                    } else{
                        x = middleToAfter.getX(y);
                    }

                } else{//middleToAfter
                    if(frontToMiddle.isHrizontal()){
                        x = result.x;
                    } else{
                        x = frontToMiddle.getX(y);
                    }
                }
                result.set(x, y);
            } else{//x y不变
                //do nothing
            }

            Point cache = new Point(result);

            //以middle为原点
            Point front1 = new Point(front.x - middle.x, front.y - middle.y);
            Point middle1 = new Point(0, 0);
            Point after1 = new Point(after.x - middle.x, after.y - middle.y);

            int location1 = MathUtil.getLocation(front1.x, front1.y);
            int location2 = MathUtil.getLocation(after1.x, after1.y);

            double degree1 = MathUtil.getDegreeFromX(frontToMiddle, location1);
            double degree2 = MathUtil.getDegreeFromX(middleToAfter, location2);

            //夹角 < 180
            double dDegree = Math.abs(degree2 - degree1);
            dDegree = Math.min(dDegree, 360 - dDegree);

            float dx = 0, dy = 0;

            //夹角的一半 < 90
            double sDegree = dDegree / 2;

            //是平移的长度
            double 长度 = distance / 2 / Math.sin(Math.toRadians(sDegree));

            //frontToMiddle和middleToAfter线的角平分线的角度
            double degree3 = (degree1 + degree2) / 2;

            if (degree3 == 90d || degree3 == 270d) {
//                float x = middle.x * trueWidth;
//                float y = middle.y * trueHeight + 1;
//                if (region.contains(x, y)) {
//                    dy = (float) 长度;
//                } else {
//                    dy = -(float) 长度;
//                }
                cache.offset(0, (float) 长度);
                if(region.contains((int)cache.x, (int)cache.y)){
                    dy = (float) 长度;
                } else{
                    dy = -(float) 长度;
                }
            } else if (degree3 == 180d) {
//                float x = middle.x * trueWidth + 1;
//                float y = middle.y * trueHeight;
//                if (region.contains(x, y)) {
//                    dx = (float) 长度;
//                } else {
//                    dx = -(float) 长度;
//                }
                cache.offset((float) 长度, 0);
                if(region.contains((int)cache.x, (int)cache.y)){
                    dx = (float) 长度;
                } else{
                    dx = -(float) 长度;
                }
            } else {
                //角平分线的斜率
                double k3 = Math.tan(Math.toRadians(degree3));

                //和X轴的夹角 <90
                double degreeWX = Math.abs(Math.toDegrees(Math.atan(k3)));// 0 - 90

                //>0
                dx = (float) (长度 * Math.cos(Math.toRadians(degreeWX)));
                dy = (float) (长度 * Math.sin(Math.toRadians(degreeWX)));

                if(edx != 0 && edy != 0){
                    if(edx < 0){
                        dx = -dx;
                    }
                    if(edy < 0){
                        dy = -dy;
                    }
                } else if(edx != 0){
                    if(edx < 0){
                        dx = -dx;
                    }
                    if (k3 > 0) {
                        if(dx > 0){
                            dy = -dy;
                        }
                    } else{
                        if(dx < 0){
                            dy = -dy;
                        }
                    }
                } else if(edy != 0){
                    if(edy < 0){
                        dy = -dy;
                    }

                    if (k3 > 0) {
                        if(dy > 0){
                            dx = -dx;
                        }
                    } else{
                        if(dy < 0){
                            dx = -dx;
                        }
                    }
                } else{
                    if (k3 > 0) {
//                    if (region.contains(x, y)) {//x+ y-
//                        dy = -dy;
//                    } else {//x- y+
//                        dx = -dx;
//                    }
                        cache.offset(- dx, dy);
                        if(region.contains((int)cache.x, (int)cache.y)){
                            dx = -dx;
                        } else{
                            dy = -dy;
                        }
                    } else {
//                    if (region.contains(x, y)) {//x+ y-
//                    } else {//x- y+
//                        dx = -dx;
//                        dy = -dy;
//                    }
                        cache.offset(- dx, - dy);
                        if(region.contains((int)cache.x, (int)cache.y)){
                            dx = -dx;
                            dy = -dy;
                        } else{
                        }
                    }
                }
            }

            result.offset(dx, dy);
            return result;
        }
    }

    /**
     * 获取默认的Region用于判断dy dx的方向
     * @param trueWidth
     * @param trueHeight
     * @return
     */
    private Region getDefaultRegion(float trueWidth, float trueHeight){
        Path defaultPath = RoundPathUtil.getFitPath(this, trueWidth, trueHeight, 0, 0);
        RectF pathRect = new RectF();
        Region defaultRegion = new Region();
        Region cacheRegion = new Region();
        RoundPathUtil.getFitPath(defaultPath, this, trueWidth, trueHeight, 0, 0);
        defaultPath.computeBounds(pathRect, false);
        cacheRegion.set((int) pathRect.left, (int) pathRect.top, (int) pathRect.right, (int) pathRect.bottom);
        defaultRegion.setPath(defaultPath, cacheRegion);
        return defaultRegion;
    }

    /**
     * 开始拖动
     */
    public void startDrag(android.graphics.Point currentTouchPoint, Point offset, DragPoint.Orientation orientation, float viewWidth, float viewHeight) {
        android.graphics.Point currentTouchPointCopy = new android.graphics.Point(currentTouchPoint);
        // 计算当前拖动位置所关联的两个点
        if (offset != null) {
            currentTouchPointCopy.offset((int) offset.x, (int) offset.y);
        }
        Point normalPoint = new Point((float) (currentTouchPointCopy.x * 1.0 / viewWidth),
                (float) (currentTouchPointCopy.y * 1.0 / viewHeight));
        boolean find = false;
        int headIndex = -1;
        int tailIndex = -1;
        // 遍历所有点连成的线，找到最佳匹配的点
        for (int i = 0; i < mLength; i++) {
            headIndex = i;
            tailIndex = i + 1 >= mLength ? 0 : i + 1;
            if (match(normalPoint, orientation, mPoints[headIndex], mPoints[tailIndex])) {
                find = true;
                break;
            }
        }
        if (find) {
            mDragHeadIndex = headIndex;
            mDragTailIndex = tailIndex;
            mDragOffset = offset;
        } else {
            mDragHeadIndex = -1;
            mDragTailIndex = -1;
            mDragOffset = null;
        }
        mCacheHeadPoint = null;
        mCacheTailPoint = null;
    }

    /**
     * 判断一个点是否在另两个点连成的线中
     * @param sourcePoint
     * @param orientation
     * @param headPoint
     * @param tailPoint
     * @return
     */
    private boolean match(Point sourcePoint, DragPoint.Orientation orientation, Point headPoint, Point tailPoint) {
        if (headPoint.x == tailPoint.x) {
            if (orientation == DragPoint.Orientation.vertical) {
                return false;
            }
            // 垂直线
            float xMatch = Math.abs(sourcePoint.x - headPoint.x);
//            float yMatch = 0;
//            if (sourcePoint.y > Math.max(headPoint.y, tailPoint.y)) {
//                yMatch = Math.abs(sourcePoint.y - Math.max(headPoint.y, tailPoint.y));
//            } else if (sourcePoint.y > Math.min(headPoint.y, tailPoint.y)) {
//                yMatch = Math.abs(sourcePoint.y - Math.min(headPoint.y, tailPoint.y));
//            }
//            return xMatch < ADMISSIBLE_ERROR && yMatch < ADMISSIBLE_ERROR;
            return xMatch < ADMISSIBLE_ERROR;
        } else if (headPoint.y == tailPoint.y) {
            if (orientation == DragPoint.Orientation.horizontal) {
                return false;
            }
            // 水平线
            float yMatch = Math.abs(sourcePoint.y - headPoint.y);
//            float xMatch = 0;
//            if (sourcePoint.x > Math.max(headPoint.x, tailPoint.x)) {
//                xMatch = Math.abs(sourcePoint.x - Math.max(headPoint.x, tailPoint.x));
//            } else if (sourcePoint.x > Math.min(headPoint.x, tailPoint.x)) {
//                xMatch = Math.abs(sourcePoint.x - Math.min(headPoint.x, tailPoint.x));
//            }
//            return xMatch < ADMISSIBLE_ERROR && yMatch < ADMISSIBLE_ERROR;
            return yMatch < ADMISSIBLE_ERROR;
        } else {
            // 普通一次函数线:y = k*x + b
            float k;
            float b;
            k = (headPoint.y - tailPoint.y) / (headPoint.x - tailPoint.x);
            b = headPoint.y - k * headPoint.x;
            if (k > 1 || k < -1) {
                // 只能水平拖动
                if (orientation == DragPoint.Orientation.vertical) {
                    return false;
                }
            } else {
                if (orientation == DragPoint.Orientation.horizontal) {
                    return false;
                }
            }
            float match = k * sourcePoint.x + b;
            return Math.abs(match - sourcePoint.y) < ADMISSIBLE_ERROR;
        }
    }

    /**
     * 拖动后更新点的位置
     *
     * @param currentTouchPoint 实际view点击的坐标
     * @param viewWidth         view的宽度
     * @param viewHeight        view的高度
     * @return 是否越界(在所有可拖动的边框中,只要有一条边越界,整个操作都不能进行,需要通过rollBackUpdatePoint回滚操作)
     */
    public boolean updateMovingPoint(android.graphics.Point currentTouchPoint, float viewWidth, float viewHeight) {
        android.graphics.Point currentTouchPointCopy = new android.graphics.Point(currentTouchPoint);
        if (mDragHeadIndex == -1 || mDragTailIndex == -1) {
            return false;
        }
        if (mDragOffset != null) {
            currentTouchPointCopy.offset((int) mDragOffset.x, (int) mDragOffset.y);
        }
        Point normalPoint = new Point((float) (currentTouchPointCopy.x * 1.0 / viewWidth),
                (float) (currentTouchPointCopy.y * 1.0 / viewHeight));
        Point headPoint = getPoint(mDragHeadIndex);
        Point tailPoint = getPoint(mDragTailIndex);
        Point headPointTemp = new Point(headPoint);
        Point tailPointTemp = new Point(tailPoint);

        // 当前需更新的两个点的相邻的点
        Point headPointBefore = getPoint(mDragHeadIndex - 1 < 0 ? getLength() - 1 : mDragHeadIndex - 1);
        Point tailPointAfter = getPoint(mDragTailIndex + 1 > getLength() - 1 ? 0 : mDragTailIndex + 1);

        // 触摸点所在的线可以看成是原来的线平移过来的，斜率不变， 根据y = kx + b 求出b,但是需要区分垂直和水平的情况
        // 原来的点所在的线垂直
        if (headPointTemp.x == tailPointTemp.x) {
            if (headPointTemp.y == headPointBefore.y) {
                headPointTemp.x = normalPoint.x;
            } else {
                float headPointK = (headPointTemp.y - headPointBefore.y) / (headPointTemp.x - headPointBefore.x);
                float headPointB = headPointTemp.y - headPointK * headPointTemp.x;
                headPointTemp.x = normalPoint.x;
                headPointTemp.y = headPointTemp.x * headPointK + headPointB;
            }
            if (tailPointTemp.y == tailPointAfter.y) {
                tailPointTemp.x = normalPoint.x;
            } else {
                float tailPointK = (tailPointTemp.y - tailPointAfter.y) / (tailPointTemp.x - tailPointAfter.x);
                float tailPointB = tailPointTemp.y - tailPointK * tailPointTemp.x;
                tailPointTemp.x = normalPoint.x;
                tailPointTemp.y = tailPointTemp.x * tailPointK + tailPointB;
            }
        }
        // 原来的点所在的线水平
        else if (headPointTemp.y == tailPointTemp.y) {
            if (headPointTemp.x == headPointBefore.x) {
                headPointTemp.y = normalPoint.y;
            } else {
                float headPointK = (headPointTemp.y - headPointBefore.y) / (headPointTemp.x - headPointBefore.x);
                float headPointB = headPointTemp.y - headPointK * headPointTemp.x;
                headPointTemp.y = normalPoint.y;
                headPointTemp.x = (headPointTemp.y - headPointB) / headPointK;
            }
            if (tailPointTemp.x == tailPointAfter.x) {
                tailPointTemp.y = normalPoint.y;
            } else {
                float tailPointK = (tailPointTemp.y - tailPointAfter.y) / (tailPointTemp.x - tailPointAfter.x);
                float tailPointB = tailPointTemp.y - tailPointK * tailPointTemp.x;
                tailPointTemp.y = normalPoint.y;
                tailPointTemp.x = (tailPointTemp.y - tailPointB) / tailPointK;
            }

        } else {
            float k = (headPointTemp.y - tailPointTemp.y) / (headPointTemp.x - tailPointTemp.x);
            float b = normalPoint.y - k * normalPoint.x;

            if (headPointTemp.x == headPointBefore.x) {
                headPointTemp.y = headPointTemp.x * k + b;
            } else if (headPointTemp.y == headPointBefore.y) {
                headPointTemp.x = (headPointTemp.y - b) / k;
            } else {
                // 原来的点所在的线与平移过来的线的交点就是新的点坐标
                float headPointK = (headPointTemp.y - headPointBefore.y) / (headPointTemp.x - headPointBefore.x);
                float headPointB = headPointTemp.y - headPointK * headPointTemp.x;
                float newHeadX = (b - headPointB) / (headPointK - k);
                headPointTemp.y = headPointK * newHeadX + headPointB;
                headPointTemp.x = newHeadX;
            }

            if (tailPointTemp.x == tailPointAfter.x) {
                tailPointTemp.y = tailPointTemp.x * k + b;
            } else if (tailPointTemp.y == tailPointAfter.y) {
                tailPointTemp.x = (tailPointTemp.y - b) / k;
            } else {
                // 原来的点所在的线与平移过来的线的交点就是新的点坐标
                float tailPointK = (tailPointTemp.y - tailPointAfter.y) / (tailPointTemp.x - tailPointAfter.x);
                float tailPointB = tailPointTemp.y - tailPointK * tailPointTemp.x;
                float newTailX = (b - tailPointB) / (tailPointK - k);
                tailPointTemp.y = tailPointK * newTailX + tailPointB;
                tailPointTemp.x = newTailX;
            }
        }

        // 边界检查
        if (!isOutOfBound(headPointTemp, headPoint, headPointBefore) &&
                !isOutOfBound(tailPointTemp, tailPoint, tailPointAfter)) {
            mCacheHeadPoint = new Point(headPoint);
            mCacheTailPoint = new Point(tailPoint);
            headPoint.set(headPointTemp);
            tailPoint.set(tailPointTemp);
            return false;
        } else {
            mCacheHeadPoint = null;
            mCacheTailPoint = null;
            return true;
        }
    }

    public void rollBackUpdatePoint() {
        if (mDragHeadIndex == -1 || mDragTailIndex == -1) {
            return;
        }
        if (mCacheHeadPoint != null) {
            getPoint(mDragHeadIndex).set(mCacheHeadPoint);
        }
        if (mCacheTailPoint != null) {
            getPoint(mDragTailIndex).set(mCacheTailPoint);
        }
    }

    /**
     * 边界检查
     * @param newPoint
     * @return
     */
    private boolean isOutOfBound(Point newPoint, Point oldPoint, Point neighborPoint) {
        boolean out = false;
        if (newPoint.x < 0) {
            out = true;
        }
        if (newPoint.x > 1) {
            out = true;
        }
        if (newPoint.y < 0) {
            out = true;
        }
        if (newPoint.y > 1) {
            out = true;
        }
        // 两点之间的间距不能小于最小值
        if (Math.abs(newPoint.x - neighborPoint.x) < MIN_MARGIN + RoundPathUtil.MAX_DISTANCE_SIZE / 1080f &&
                Math.abs(newPoint.y - neighborPoint.y) < MIN_MARGIN + RoundPathUtil.MAX_DISTANCE_SIZE / 1080f) {
            out = true;
        }
        // 不能越过起始点
        if (oldPoint.x > neighborPoint.x) {
            if (newPoint.x <= neighborPoint.x) {
                out = true;
            }
        } else if (oldPoint.x < neighborPoint.x) {
            if (newPoint.x >= neighborPoint.x) {
                out = true;
            }
        }
        if (oldPoint.y > neighborPoint.y) {
            if (newPoint.y <= neighborPoint.y) {
                out = true;
            }
        } else if (oldPoint.y < neighborPoint.y) {
            if (newPoint.y >= neighborPoint.y) {
                out = true;
            }
        }


        return out;
    }

    /**
     * 停止拖动
     */
    public void stopDrag() {
        mDragHeadIndex = -1;
        mDragTailIndex = -1;
        mCacheHeadPoint = null;
        mCacheTailPoint = null;
        mDragOffset = null;
    }


    @Override
    public boolean equals(Object obj) {
        if (obj == null || !(obj instanceof CollagePoints)) {
            return false;
        }
        if (obj == this) {
            return true;
        }
        CollagePoints that = (CollagePoints) obj;
        if (this.mLength == that.mLength) {
            for (int i = 0; i < mLength; i++) {
                Point thisPoint = getPoint(i);
                Point thatPoint = that.getPoint(i);
                if (thisPoint == thatPoint) {
                    continue;
                }
                return !(thisPoint == null || thatPoint == null) && thisPoint.equals(thatPoint);
            }
        }
        return false;
    }

    @Override
    protected CollagePoints clone() {
        CollagePoints collagePoints = new CollagePoints();
        if (mPoints != null && mPoints.length > 0) {
            for (Point point : mPoints) {
                collagePoints.addPoint(point.clone());
            }
        }
        return collagePoints;
    }
}
